package color;

import geom.Vector;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;

//import org.apache.log4j.Logger;




import processing.core.PApplet;
import proxml.XMLElement;


public class Rgb{
	public enum Metric{ SQUARED, MANHATTAN; }
	public static Metric metric = Metric.SQUARED;
	public static float MAX_RGB = 255f;
	public static float maxSquared = (float)Math.sqrt(3 * Math.pow(MAX_RGB, 2));
	public static float maxManhattan = MAX_RGB * 3;
	
	public float r, g, b;
	
	public Rgb(int rgb){
		this(ColorConverter.unpackRgb(rgb));
	}
	
	public Rgb(Rgb rgb){ 
		this(rgb.r, rgb.g, rgb.b); 
	}
	
	public Rgb (Vector v){ 
		this(v.x, v.y, v.z);
	}
	
	public float distanceTo(Rgb b, Metric metric){
		switch(metric){
		case SQUARED: 	return squared(this, b);
		case MANHATTAN: return manhattan(this, b);
		default: 		return Float.NaN;
		}
	}
	
	public Rgb (float r, float g, float b){ 
		this.r = r; this.g = g;this.b = b; 
	}
	
	public String toString(){ 
		return "RGB:{"+r+", "+g+", "+b+"}"; 
	}
	
	
	public static float squared(Rgb a, Rgb b){ 
		return (float) Math.sqrt(
			Math.pow(a.r - b.r, 2) 
			+ Math.pow(a.g - b.g, 2) 
			+ Math.pow(a.b - b.b, 2)
		);
	}
	
	
	public static float manhattan(Rgb a, Rgb b){ 
		return 
			Math.abs(a.r - b.r)
			+ Math.abs(a.g - b.g)
			+ Math.abs(a.b - b.b);
	}
	
	public static Rgb average(ArrayList<Rgb> labs){
		Iterator<Rgb> i = labs.iterator();
		Rgb avg = new Rgb(0, 0, 0);
		while(i.hasNext()){
			Rgb next = i.next();
			avg.r += next.r;
			avg.g += next.g;
			avg.b += next.b;
		}
		avg.r /= labs.size();
		avg.g /= labs.size();
		avg.b /= labs.size();
		return avg;
	}
	
	@Override
	public int hashCode() {
		final int PRIME = 31;
		int result = 1;
		result = PRIME * result + Float.floatToIntBits(b);
		result = PRIME * result + Float.floatToIntBits(g);
		result = PRIME * result + Float.floatToIntBits(r);
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final Rgb other = (Rgb) obj;
		if (Float.floatToIntBits(b) != Float.floatToIntBits(other.b))
			return false;
		if (Float.floatToIntBits(g) != Float.floatToIntBits(other.g))
			return false;
		if (Float.floatToIntBits(r) != Float.floatToIntBits(other.r))
			return false;
		return true;
	}
	
	public Vector directionVector(Rgb to){
		float r = this.r - to.r;
		float g = this.g - to.g;
		float b = this.b - to.b;
		float abs = (float)Math.sqrt(r*r + g*g + b*b);
		return new Vector(r / abs, g / abs, b / abs);
	}
	
	public Vector getVector(){
		return new Vector(r, g, b);
	}
	
	public float getVectorPos(Vector v){
		return (r * v.x + g * v.y + b * v.z) / v.absLength();
	}
	
	
	
	public void fill(PApplet pa){
		pa.fill(r, g, b);
	}
	
	public void stroke(PApplet pa){
		pa.stroke(r, g, b);
	}
	
	public void emissive(PApplet pa){
		pa.emissive(r, g, b);
	}
	
	public void specular(PApplet pa){
		pa.specular(r, g, b);
	}
	public void ambient(PApplet pa){
		pa.ambient(r, g, b);
	}
	
	@SuppressWarnings("hiding")
	public class RedComparator implements Comparator<Color>{
		/**
		 * Logger for this class
		 */
		//private final Logger logger = Logger.getLogger(RedComparator.class);

		public int compare(Color arg0, Color arg1) {
			float r0 = arg0.getRed();
			float r1 = arg1.getRed();
			if(r0 < r1) return -1;
			else if(r1 < r0) return 1;
			return 0;
		}
	}
	
	public class GrnComparator implements Comparator<Color>{
		/**
		 * Logger for this class
		 */
		//private final Logger logger = Logger.getLogger(GrnComparator.class);

		public int compare(Color arg0, Color arg1) {
			float r0 = arg0.getGrn();
			float r1 = arg1.getGrn();
			if(r0 < r1) return -1;
			else if(r1 < r0) return 1;
			return 0;
		}
	}
	
	public class BluComparator implements Comparator<Color>{
		/**
		 * Logger for this class
		 */
		//private final Logger logger = Logger.getLogger(BluComparator.class);

		public int compare(Color arg0, Color arg1) {
			float r0 = arg0.getBlu();
			float r1 = arg1.getBlu();
			if(r0 < r1) return -1;
			else if(r1 < r0) return 1;
			return 0;
		}
	}
	
	public XMLElement toXML(){
		XMLElement elem = new XMLElement("LabClr");
		elem.addAttribute("r", r);
		elem.addAttribute("g", g);
		elem.addAttribute("b", b);
		return elem;
	}
	
	public Rgb getOppositeBrightness(){
		return new Rgb((r + 128f) % 256f, (r + 128f) % 256f, (r + 128f) % 256f);
	}
}